{***************************************************************************}
{                                                                           }
{           Spring Framework for Delphi                                     }
{                                                                           }
{           Copyright (C) 2009-2011 DevJET                                  }
{                                                                           }
{           http://www.spring4d.org                                         }
{                                                                           }
{***************************************************************************}
{                                                                           }
{  Licensed under the Apache License, Version 2.0 (the "License");          }
{  you may not use this file except in compliance with the License.         }
{  You may obtain a copy of the License at                                  }
{                                                                           }
{      http://www.apache.org/licenses/LICENSE-2.0                           }
{                                                                           }
{  Unless required by applicable law or agreed to in writing, software      }
{  distributed under the License is distributed on an "AS IS" BASIS,        }
{  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. }
{  See the License for the specific language governing permissions and      }
{  limitations under the License.                                           }
{                                                                           }
{***************************************************************************}

unit Spring.Serialization.Backend.lkJSON;

{$I Spring.inc}

interface
uses
  uLkJSON,
  Classes,
  SysUtils,
  Rtti,
  Generics.Collections,
  Variants,
  Spring,
  Spring.Collections,
  Spring.Serialization.Backend;

type
  TXmlBackend_lkJSON = class(TBackendBase, IBackend, IInterface)
  protected
    procedure ContentToTree(const stream: TStream; const document: IBackendDocument); override;
    procedure TreeToContent(const stream: TStream; const document: IBackendDocument); override;
  end;

  procedure NodeToJSONlist(const node: IBackendNode; const JSONlist: TlkJSONlist);
  procedure NodeToJSONobject(const node: IBackendNode; const JSONobject: TlkJSONobject);
  procedure JSONlistToNode(const JSONlist: TlkJSONlist; const node: IBackendNode);
  procedure JSONobjectToNode(const JSONobject: TlkJSONobject; const node: IBackendNode);

implementation

uses
  Spring.Serialization,
  Spring.Serialization.ResourceStrings;

procedure NodeToJSONlist(const node: IBackendNode; const JSONlist: TlkJSONlist);
var
  child: IBackendNode;
  obj: TlkJSONobject;
begin
  for child in node.Children do
  begin
    obj := TlkJSONobject.Create;
    JSONlist.Add(obj);
    NodeToJSONobject(child, obj);
  end;
end;

procedure NodeToJSONobject(const node: IBackendNode; const JSONobject: TlkJSONobject);
var
  attributeName: string;
  child: IBackendNode;
  list: TlkJSONlist;
  obj: TlkJSONobject;
begin
  for attributeName in node.Attributes.Keys do
    JSONobject.Add(attributeName, node.Attributes.Items[attributeName]);

  if node.Children.IsEmpty then
    if node.NodeValue <> '' then
      JSONobject.Add(CNodeValueAttributeName, node.NodeValue)
    else
  else
    for child in node.Children do
    begin
      if child.NodeName = CItemsCollectionCodeName then
      begin
        list := TlkJSONlist.Create;
        JSONobject.Add(child.NodeName, list);
        NodeToJSONlist(child, list);
      end
      else
      begin
        obj := TlkJSONobject.Create;
        JSONobject.Add(child.NodeName, obj);
        NodeToJSONobject(child, obj);
      end;
    end;
end;

procedure JSONlistToNode(const JSONlist: TlkJSONlist; const node: IBackendNode);
var
  i: integer;
  newNode: IBackendNode;
begin
  for i := 0 to JSONlist.Count - 1 do
  begin
    if (JSONlist.Child[i].SelfType in [jsObject]) then
    begin
      newNode := node.AddChild;
      newNode.NodeName := CItemCollectionCodeName;
      JSONobjectToNode(JSONlist.Child[i] as TlkJSONobject, newNode);
    end;
  end;
end;

procedure JSONobjectToNode(const JSONobject: TlkJSONobject; const node: IBackendNode);
var
  i: integer;
  newNode: IBackendNode;
begin
  for i := 0 to JSONobject.Count - 1 do
  begin
    if (JSONobject.FieldByIndex[i].SelfType in [jsNumber, jsString, jsBoolean, jsNull]) then
      if JSONobject.NameOf[i] = CNodeValueAttributeName then
        node.NodeValue := JSONobject.FieldByIndex[i].Value
      else
        node.Attributes.AddOrSetValue(JSONobject.NameOf[i], JSONobject.FieldByIndex[i].Value);

    if (JSONobject.FieldByIndex[i].SelfType in [jsObject]) then
    begin
      newNode := node.AddChild;
      newNode.NodeName := JSONobject.NameOf[i];
      JSONobjectToNode(JSONobject.FieldByIndex[i] as TlkJSONobject, newNode);
    end;

    if (JSONobject.FieldByIndex[i].SelfType in [jsList]) then
    begin
      newNode := node.AddChild;
      newNode.NodeName := CItemsCollectionCodeName;
      JSONlistToNode(JSONobject.FieldByIndex[i] as TlkJSONlist, newNode);
    end;
  end;
end;

{ TXmlBackend_TXmlDocument }

procedure TXmlBackend_lkJSON.TreeToContent(const stream: TStream;
  const document: IBackendDocument);
var
  js: TlkJSONobject;
  root: TlkJSONobject;
  stringData: string;
  stringStream: TStringStream;
begin
  js := TlkJSONobject.Create;
  NodeToJSONobject(document.Root.Children.First, js);
  stringData := TlkJSONstreamed.GenerateText(js);
  stringStream := TStringStream.Create(stringData);
  stringStream.SaveToStream(stream);
  stringStream.Free;
  js.Free;
end;

procedure TXmlBackend_lkJSON.ContentToTree(const stream: TStream;
  const document: IBackendDocument);
var
  js: TlkJSONobject;
  node: IBackendNode;
  stringStream: TStringStream;
begin
  try
    stringStream := TStringStream.Create;
    stringStream.LoadFromStream(stream);
    js := TlkJSONstreamed.ParseText(stringStream.DataString) as TlkJSONobject;
    stringStream.Free;
    if not Assigned(js) then
      raise Exception.Create('Loading JSON failed');
  except
    raise ESerializationSourceCodeException.CreateRes(@SCodeNotFound);
  end;
  node := document.Root.AddChild;
  node.NodeName := CInstanceCollectionCodeName;
  JSONobjectToNode(js, node);
  js.Free;
end;

end.
